using System;
using System.IO;
using System.Collections;
using System.Security.Cryptography;
namespace Magic.Core
{
    /// <summary>
    /// Represents a file consisting of metadata and optionally a header.
    /// </summary>
    /// 

    public class Tag
    {
        private Xpe xpe;
        private int offset;
        private Array values;
        private EndianReader er;
        private int typeCounter = 0;
        private string name = null;
        private object reservedTypeData = null;
        public bool isprotected = false;

        public delegate void TagChanged(Tag sender);
        public event TagChanged TagHasChanged;
        public bool isDirty = false;

        public class DataContainer
        {
            public int variable;
            public byte[] data;

            public DataContainer(int var, byte[] dat)
            {
                variable = var;
                data = dat;
            }
        }
        public enum ScriptValueType : short
        {
            unparsed,
            special_form,
            function_name,
            passthrough,
            @void,
            boolean,
            real,
            @short,
            @long,
            @string,
            script,
            trigger_volume,
            cutscene_flag,
            cutscene_camera_point,
            cutscene_title,
            cutscene_recording,
            device_group,
            ai,
            ai_command_list,
            starting_profile,
            conversation,
            navpoint,
            hud_message,
            object_list,
            sound,
            effect,
            damage,
            looping_sound,
            animation_graph,
            actor_variant,
            damage_effect,
            object_definition,
            game_difficulty,
            team,
            ai_default_state,
            actor_type,
            hud_corner,
            @object,
            unit,
            vehicle,
            weapon,
            device,
            scenery,
            object_name,
            unit_name,
            vehicle_name,
            weapon_name,
            device_name,
            scenery_name
        }

        public Tag(string iname, string directory, TagLibrary lib, string ceTagsDirectory, bool tagHash)
        {
            name = iname.Replace(directory + "\\", "");

            er = new EndianReader(new FileStream(iname, FileMode.Open, FileAccess.Read, FileShare.Read), ByteOrder.BigEndian);
           byte[] temp = er.ReadBytes(er.Length);
           er.Close();
           er = new EndianReader(new MemoryStream(temp),ByteOrder.BigEndian);
            ReadFromTagFile(directory, Path.GetFileName(iname), lib, ceTagsDirectory, tagHash);
            er.Close(); er = null;
        }
        public Tag(string iname, Xpe type)
        {
            name = iname;
            xpe = type;
            values = Array.CreateInstance(typeof(object), xpe.Children.Length);
            for (int x = 0; x < xpe.Children.Length; x++)
                values.SetValue(Xpe.DefaultValue(xpe.Children[x].Type, xpe.Children[x].Size, xpe.Children[x].Children.Length), x);
        }
        public Tag(EndianReader inEr, string iname, string type, uint magic, TagLibrary lib)
        {
            er = inEr;
            name = iname;
            ReadFromMapFile(type, name, lib, magic);
        }

        public void Update()
        {
            Dirty();
            if (!(TagHasChanged == null))
            {
                TagHasChanged(this);
            }
        }
        public void Dirty()
        {
            isDirty = true;
        }

        public string Name
        {
            get
            {
                return name;
            }
        }
        public string Alias(HaloMap.MapUsage usage)
        {
            if (xpe.FourCC == "matg")
            {
                switch (usage)
                {
                    case HaloMap.MapUsage.Campaign:
                        return "globals\\campaign";

                    case HaloMap.MapUsage.Multiplayer:
                        return "globals\\multiplayer";

                    case HaloMap.MapUsage.UI:
                        return "globals\\main_menu";

                    default:
                        return "globals\\globals";
                }
            }
            else
                return name.Trim((char)0);
        }
        public object this[string name]
        {
            get
            {
                Xpe.Field[] children = xpe.Children;
                for (int x = 0; x < children.Length; x++)
                    if (children[x].Name == name)
                        return values.GetValue(x);
                return null;
            }
            set
            {
                Xpe.Field[] children = xpe.Children;
                for (int x = 0; x < children.Length; x++)
                {
                    if (children[x].Name == name)
                    {
                        values.SetValue(value, x);
                        return;
                    }
                }
            }
        }
        public object this[int x]
        {
            get
            {
                if (x == -1)
                    return null;
                return values.GetValue(x);
            }
            set
            {
                values.SetValue(value, x);
                return;
            }
        }
        public void Write(string filename, TagLibrary lib)
        {
            EndianWriter ew = new EndianWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), ByteOrder.BigEndian);

            typeCounter = 0;
            ew.Position = 0;
            WriteHeader(ew);
            int privateOffset = ew.Position;
            uint absoluteEnd = (uint)xpe.HeaderLength + (uint)privateOffset;
            Xpe.Field[] children = xpe.Children;
            ew.Length += xpe.HeaderLength;
            for (int x = 0; x < children.Length; x++)
                RecursiveWriteFile(ew, children[x], values, -1, x, lib, privateOffset, ref absoluteEnd, false);
            ew.Close(); ew = null;
            isDirty = false;
        }

        public void Write(string filename, TagLibrary lib, Xpe newDef)
        {
            EndianWriter ew = new EndianWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), ByteOrder.BigEndian);

            typeCounter = 0;
            ew.Position = 0;
            WriteHeader(ew,newDef);
            int privateOffset = ew.Position;
            uint absoluteEnd = (uint)newDef.HeaderLength + (uint)privateOffset;
            Xpe.Field[] children = newDef.Children;
            ew.Length += newDef.HeaderLength;
            for (int x = 0; x < children.Length; x++)
                RecursiveWriteFile(ew, children[x], values, -1, x, lib, privateOffset, ref absoluteEnd, false);
            ew.Close(); ew = null;
            isDirty = false;
        }

        public byte[] ReturnHash(TagLibrary lib)
        {
            EndianWriter ew = new EndianWriter(new MemoryStream(), ByteOrder.BigEndian);

            typeCounter = 0;
            ew.Position = 0;
            WriteHeader(ew);
            int privateOffset = ew.Position;
            uint absoluteEnd = (uint)xpe.HeaderLength + (uint)privateOffset;
            Xpe.Field[] children = xpe.Children;
            ew.Length += xpe.HeaderLength;
            for (int x = 0; x < children.Length; x++)
                RecursiveWriteFile(ew, children[x], values, -1, x, lib, privateOffset, ref absoluteEnd, true);

            MD5 nMD5 = MD5.Create();
            ew.BaseStream.Position = 0;
            byte[] md5Hash = nMD5.ComputeHash(ew.BaseStream);

            ew.Close();
            ew = null;
            return md5Hash;
        }
        public int CheckHash(TagLibrary lib)
        {
            if (isprotected == true)
            {
                EndianWriter ew;

                ew = new EndianWriter(new MemoryStream(), ByteOrder.BigEndian);

                typeCounter = 0;
                ew.Position = 0;
                WriteHeader(ew);
                int privateOffset = ew.Position;
                uint absoluteEnd = (uint)xpe.HeaderLength + (uint)privateOffset;
                Xpe.Field[] children = xpe.Children;
                ew.Length += xpe.HeaderLength;
                for (int x = 0; x < children.Length; x++)
                    RecursiveWriteFile(ew, children[x], values, -1, x, lib, privateOffset, ref absoluteEnd, true);

                MD5 nMD5 = MD5.Create();
                ew.BaseStream.Position = 0;
                byte[] md5Hash = nMD5.ComputeHash(ew.BaseStream);
                int tagIndex = lib.CXPE.md5Hash.IndexOf(md5Hash);

                if (tagIndex != -1)
                {
                    Console.WriteLine(lib.CXPE.md5Name[tagIndex].ToString() + " matches " + this.name.Trim('\0') + "." + xpe.Extension);
                }

                ew.Close();
                ew = null;
                return tagIndex;
            }
            return -1;
        }
        private void WriteHeader(EndianWriter ew)
        {
            for (sbyte x = 0; x < 9; x++)
                ew.Write(0);
            ew.WriteFourCC(xpe.FourCC);
            ew.Write(0);
            ew.Write(64);
            ew.Write(0);
            ew.Write(0);
            ew.Write(xpe.Version);
            ew.Write((short)255);
            ew.Write(0x626c616d); // 'blam'
        }
        private void WriteHeader(EndianWriter ew,Xpe newDef)
        {
            for (sbyte x = 0; x < 9; x++)
                ew.Write(0);
            ew.WriteFourCC(newDef.FourCC);
            ew.Write(0);
            ew.Write(64);
            ew.Write(0);
            ew.Write(0);
            ew.Write(newDef.Version);
            ew.Write((short)255);
            ew.Write(0x626c616d); // 'blam'
        }

        public void Write(EndianWriter ew, uint memory, TagLibrary lib, int[] aux)
        {
            typeCounter = 0;
            int privateOffset = ew.Position;
            uint absoluteEnd = (uint)xpe.HeaderLength + (uint)privateOffset;
            Xpe.Field[] children = xpe.Children;
            ew.Length += xpe.HeaderLength;

            switch (xpe.FourCC)
            {
                case "scnr":
                    DataContainer syntaxData = values.GetValue(xpe.IndexByName("script syntax data")) as DataContainer;
                    EndianReader syntaxReader = new EndianReader(new MemoryStream(syntaxData.data), ByteOrder.BigEndian);
                    DataContainer stringData = values.GetValue(xpe.IndexByName("script string data")) as DataContainer;
                    EndianReader stringReader = new EndianReader(new MemoryStream(stringData.data), ByteOrder.LittleEndian);
                    MemoryStream reverseStream = new MemoryStream(syntaxData.data);
                    EndianWriter reverseWriter = new Magic.Core.EndianWriter(reverseStream, Magic.Core.ByteOrder.LittleEndian);

                    reverseWriter.Write(syntaxReader.ReadBytes(32));
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadBytes(4));
                    reverseWriter.Write(syntaxReader.ReadInt32());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    short maxNum = syntaxReader.ReadInt16();
                    reverseWriter.Write(maxNum);
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadUInt32());

                    for (int i = 0; i < maxNum; i++)
                    {
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        ScriptValueType valueType = (ScriptValueType)syntaxReader.ReadInt16();
                        reverseWriter.Write((short)valueType);
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt32());
                        int stringOffset = syntaxReader.ReadInt32();
                        reverseWriter.Write(stringOffset);
                        uint valueData = syntaxReader.ReadUInt32();
                        string tagExtension = "";
                        switch (valueType)
                        {
                            case ScriptValueType.looping_sound:
                                tagExtension = "sound_looping";
                                break;
                            case ScriptValueType.sound:
                                tagExtension = "sound";
                                break;
                            case ScriptValueType.effect:
                                tagExtension = "effect";
                                break;
                            case ScriptValueType.actor_variant:
                                tagExtension = "actor_variant";
                                break;
                            case ScriptValueType.animation_graph:
                                tagExtension = "model_animations";
                                break;
                            case ScriptValueType.damage:
                                tagExtension = "damage_effect";
                                break;
                            case ScriptValueType.object_definition:
                                tagExtension = "obje";
                                break;
                        }
                        if (tagExtension != "")
                        {
                            stringReader.Position = stringOffset;
                            string tagName = stringReader.ReadString();
                            int tagIndex = -1;
                            if (valueType == ScriptValueType.object_definition)
                            {
                                foreach (Magic.Core.Xpe xpeItem in lib.CXPE.InheritsFourCC(tagExtension))
                                {
                                    tagIndex = lib.IndexByName(tagName, xpeItem.FourCC);
                                    if (tagIndex > -1)
                                        break;
                                }
                            }
                            else
                            {
                                tagIndex = lib.IndexByName(tagName, lib.CXPE.FromExtension(tagExtension).FourCC);
                            }
                            if (tagIndex > -1)
                            {
                                valueData = lib.GetID(tagIndex);
                            }
                        }
                        reverseWriter.Write(valueData);
                    }

                    reverseStream.Position = 0;
                    syntaxData.data = reverseStream.ToArray();
                    values.SetValue(syntaxData, xpe.IndexByName("script syntax data"));
                    reverseWriter.Close();
                    stringReader.Close();
                    syntaxReader.Close();
                    break;
            }

            for (int x = 0; x < children.Length; x++)
                RecursiveWriteMap(ew, children[x], values, -1, x, lib, privateOffset, ref absoluteEnd, aux, memory);
        }

        public void Write(EndianWriter ew, TagLibrary lib, ArrayList pointers, ArrayList destinations, int[] aux)
        {
            typeCounter = 0;
            int privateOffset = ew.Position;
            uint absoluteEnd = (uint)xpe.HeaderLength + (uint)privateOffset;
            Xpe.Field[] children = xpe.Children;
            ew.Length += xpe.HeaderLength;

            switch (xpe.FourCC)
            {
                case "scnr":
                    DataContainer syntaxData = values.GetValue(xpe.IndexByName("script syntax data")) as DataContainer;
                    EndianReader syntaxReader = new EndianReader(new MemoryStream(syntaxData.data), ByteOrder.BigEndian);
                    DataContainer stringData = values.GetValue(xpe.IndexByName("script string data")) as DataContainer;
                    EndianReader stringReader = new EndianReader(new MemoryStream(stringData.data), ByteOrder.LittleEndian);
                    MemoryStream reverseStream = new MemoryStream(syntaxData.data);
                    EndianWriter reverseWriter = new Magic.Core.EndianWriter(reverseStream, Magic.Core.ByteOrder.LittleEndian);

                    reverseWriter.Write(syntaxReader.ReadBytes(32));
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadBytes(4));
                    reverseWriter.Write(syntaxReader.ReadInt32());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    short maxNum = syntaxReader.ReadInt16();
                    reverseWriter.Write(maxNum);
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadUInt32());

                    for (int i = 0; i < maxNum; i++)
                    {
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        ScriptValueType valueType = (ScriptValueType)syntaxReader.ReadInt16();
                        reverseWriter.Write((short)valueType);
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt32());
                        int stringOffset = syntaxReader.ReadInt32();
                        reverseWriter.Write(stringOffset);
                        uint valueData = syntaxReader.ReadUInt32();
                        string tagExtension = "";
                        switch (valueType)
                        {
                            case ScriptValueType.looping_sound:
                                tagExtension = "sound_looping";
                                break;
                            case ScriptValueType.sound:
                                tagExtension = "sound";
                                break;
                            case ScriptValueType.effect:
                                tagExtension = "effect";
                                break;
                            case ScriptValueType.actor_variant:
                                tagExtension = "actor_variant";
                                break;
                            case ScriptValueType.animation_graph:
                                tagExtension = "model_animations";
                                break;
                            case ScriptValueType.damage:
                                tagExtension = "damage_effect";
                                break;
                            case ScriptValueType.object_definition:
                                tagExtension = "obje";
                                break;
                        }
                        if (tagExtension != "")
                        {
                            stringReader.Position = stringOffset;
                            string tagName = stringReader.ReadString();
                            int tagIndex = -1;
                            if (valueType == ScriptValueType.object_definition)
                            {
                                foreach (Magic.Core.Xpe xpeItem in lib.CXPE.InheritsFourCC(tagExtension))
                                {
                                    tagIndex = lib.IndexByName(tagName, xpeItem.FourCC);
                                    if (tagIndex > -1)
                                        break;
                                }
                            }
                            else
                            {
                                tagIndex = lib.IndexByName(tagName, lib.CXPE.FromExtension(tagExtension).FourCC);
                            }
                            if (tagIndex > -1)
                            {
                                valueData = lib.GetID(tagIndex);
                            }
                        }
                        reverseWriter.Write(valueData);
                    }

                    reverseStream.Position = 0;
                    syntaxData.data = reverseStream.ToArray();
                    values.SetValue(syntaxData, xpe.IndexByName("script syntax data"));
                    reverseWriter.Close();
                    stringReader.Close();
                    syntaxReader.Close();
                    break;
            }

            for (int x = 0; x < children.Length; x++)
                RecursiveWriteMap(ew, children[x], values, -1, x, lib, privateOffset, ref absoluteEnd, aux, pointers, destinations);
        }

        private void ReadFromTagFile(string directory, string filename, TagLibrary lib, string ceTagsDirectory, bool tagHash)
        {
            // Set the offset to the length of the header, 64
            offset = 0x40;
            // Read in from the header the type of tag that this is
            er.Position = 0x24;
            string type = er.ReadFourCC();
            xpe = lib.CXPE[type];
            // Read in from the header this tag's version, to check with the internal directory
            er.Position = 0x38;
            short version = er.ReadInt16();
            if (version != xpe.Version)
                throw new Exception("Tag version read was not the version expected.\nFile: " + filename + "\nVersion Expected: " + Convert.ToString(xpe.Version, 10) + "\nVersion Read: " + Convert.ToString(version, 10));

            // Read all dependencies for the tag; ignore other fields
            int absoluteEnd = offset + xpe.HeaderLength;
            Xpe.Field[] fields = xpe.Children;
            values = Array.CreateInstance(typeof(object), xpe.Children.Length);
            for (int x = 0; x < fields.Length; x++)
                values.SetValue(RecursiveReadFile(fields[x], lib, directory, offset, ref absoluteEnd, ceTagsDirectory, tagHash), x);
        }


        private void ReadFromMapFile(string type, string name, TagLibrary lib, uint magic)
        {
            // Set the offset to the offset of the tag
            offset = er.Position;
            xpe = lib.CXPE[type];

            // Prepare for a model data influx
            if (type == "mode" || type == "mod2")
                reservedTypeData = new ArrayList();

            // Read all dependencies for the tag; ignore other fields
            int absoluteEnd = offset + xpe.HeaderLength;
            Xpe.Field[] fields = xpe.Children;
            values = Array.CreateInstance(typeof(object), xpe.Children.Length);
            for (int x = 0; x < fields.Length; x++)
                values.SetValue(RecursiveReadMap(fields[x], lib, offset, magic), x);

            // Take care of any unfinished raw data business
            switch (type)
            {
                case "bitm":
                    byte[] pixelData = new byte[Convert.ToInt32(reservedTypeData)];

                    Array bitmaps = values.GetValue(xpe.IndexByName("bitmaps")) as Array;
                    int count = bitmaps.GetLength(0);
                    int doffset = 0;
                    for (int c = 0; c < count; c++)
                    {
                        int[] Desc = (int[])bitmaps.GetValue(c, 10);
                        er.Position = Desc[0];
                        Array.Copy(er.ReadBytes(Desc[1]), 0, pixelData, doffset, Desc[1]);
                        doffset += Desc[1];
                        bitmaps.SetValue(Desc[2], c, 10);
                    }

                    reservedTypeData = null;

                    int pixelLength = pixelData.GetLength(0);

                    int bmc = bitmaps.GetLength(0);
                    for (int b = 0; b < bmc; b++)
                    {

                        byte tflags = (byte)(short)bitmaps.GetValue(b, 6);
                        if ((tflags & 8) == 0)
                            continue;
                        short tformat = (short)bitmaps.GetValue(b, 5);
                        if ((tformat > 13 && tformat < 17) || tformat == 9)
                            continue;
                        short ttype = (short)bitmaps.GetValue(b, 4);
                        short mips = (short)bitmaps.GetValue(b, 8);
                        int Offset = (int)bitmaps.GetValue(b, 10);
                        Console.WriteLine(tformat.ToString() + " " + HaloMap.BitmapBpp(tformat).ToString());
                        if (mips > 0)
                        {
                            int mipSize = 0;
                            for (int m = 0; m < mips; m++)
                            {
                                double width = (short)bitmaps.GetValue(b, 1) / (short)Math.Pow(2.0, (double)m);
                                double height = (short)bitmaps.GetValue(b, 2) / (short)Math.Pow(2.0, (double)m);
                                pixelData = Swizzler.Swizzle(pixelData as byte[], Offset + mipSize, (short)width, (short)height, (short)bitmaps.GetValue(b, 3), HaloMap.BitmapBpp(tformat), true);
                                mipSize += Tag.CalculateMipmapSize((short)bitmaps.GetValue(b, 1), (short)bitmaps.GetValue(b, 2), (short)bitmaps.GetValue(b, 3), ttype, tformat, (short)m);
                            }
                        }
                        else
                        {
                            pixelData = Swizzler.Swizzle(pixelData as byte[], Offset, (short)bitmaps.GetValue(b, 1), (short)bitmaps.GetValue(b, 2), (short)bitmaps.GetValue(b, 3), HaloMap.BitmapBpp(tformat), true);
                        }
                        bitmaps.SetValue((short)(tflags ^ 0x8), b, 6); // TODO: Figure out what a lot of these are...
                        //bitmaps.SetValue(new byte[4] { 0xff, 0xff, 0xff, 0xff }, b, 12);
                        //bitmaps.SetValue(new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x27, 0x02 }, b, 13);
                    }


                    values.SetValue(bitmaps, xpe.IndexByName("bitmaps"));
                    values.SetValue(pixelData, xpe.IndexByName("processed pixel data"));
                    typeCounter = 0;

                    break;

                //er.Position = typeCounter;
                //byte[] pixelData = er.ReadBytes(Convert.ToInt32(reservedTypeData));
                //values.SetValue(pixelData, xpe.IndexByName("processed pixel data"));
                //typeCounter = 0;
                //reservedTypeData = null;
                //return;

                case "mode":
                case "mod2":
                    ArrayList modelData = reservedTypeData as ArrayList;
                    Array geometries = values.GetValue(xpe.IndexByName("geometries")) as Array;
                    Xpe.Field geometry = xpe.FieldByName("geometries");
                    Xpe.Field part = geometry.FieldByName("parts");
                    int partsIndex = geometry["parts"];
                    int compressedIndex = part["compressed vertices"];
                    int uncompressedIndex = part["uncompressed vertices"];
                    int trianglesIndex = part["triangles"];
                    int geometryCount = geometries.GetLength(0);
                    int passCount = 0;

                    for (int u = 0; u < geometryCount; u++)
                    {
                        Array parts = geometries.GetValue(u, partsIndex) as Array;
                        int partCount = parts.GetLength(0);

                        for (int v = 0; v < partCount; v++)
                        {
                            parts.SetValue(modelData[(passCount * 2) + 1], v, compressedIndex);
                            //Array decompressedVerts = Array.CreateInstance(typeof(object), 0, 9);
                            Array decompressedVerts = Array.CreateInstance(typeof(object), (modelData[(passCount * 2) + 1] as Array).GetLength(0), 9);
                            for (int i = 0; i < decompressedVerts.GetLength(0); i++)
                            {
                                //Position
                                decompressedVerts.SetValue((modelData[(passCount * 2) + 1] as Array).GetValue(i, 0), i, 0);
                                //Normal, BiNormal, Tangent
                                decompressedVerts.SetValue(HaloMap.Decompress111110((int)(modelData[(passCount * 2) + 1] as Array).GetValue(i, 1)), i, 1);
                                decompressedVerts.SetValue(HaloMap.Decompress111110((int)(modelData[(passCount * 2) + 1] as Array).GetValue(i, 2)), i, 2);
                                decompressedVerts.SetValue(HaloMap.Decompress111110((int)(modelData[(passCount * 2) + 1] as Array).GetValue(i, 3)), i, 3);
                                //Texture Coords
                                decompressedVerts.SetValue(new float[] { HaloMap.DecompressHalf16((short)(modelData[(passCount * 2) + 1] as Array).GetValue(i, 4)), HaloMap.DecompressHalf16((short)(modelData[(passCount * 2) + 1] as Array).GetValue(i, 5)) }, i, 4);
                                //Node Index
                                decompressedVerts.SetValue((short)((sbyte)(byte)((modelData[(passCount * 2) + 1] as Array).GetValue(i, 6))  / 3), i, 5);
                                decompressedVerts.SetValue((short)((sbyte)(byte)((modelData[(passCount * 2) + 1] as Array).GetValue(i, 7)) / 3), i, 6);
                                //Node Weights
                                decompressedVerts.SetValue(HaloMap.DecompressHalf16((short)(modelData[(passCount * 2) + 1] as Array).GetValue(i, 8)), i, 7);
                                decompressedVerts.SetValue(0f, i, 8);
                            }
                            parts.SetValue(decompressedVerts, v, uncompressedIndex);
                            parts.SetValue(modelData[(passCount++) * 2], v, trianglesIndex);
                        }
                    }
                    reservedTypeData = null;
                    return;
                case "sbsp":

                    return;
                case "scnr":
                    DataContainer syntaxData = values.GetValue(xpe.IndexByName("script syntax data")) as DataContainer;
                    BinaryReader syntaxReader = new BinaryReader(new MemoryStream(syntaxData.data));
                    MemoryStream reverseStream = new MemoryStream(syntaxData.data);
                    EndianWriter reverseWriter = new Magic.Core.EndianWriter(reverseStream, Magic.Core.ByteOrder.BigEndian);

                    reverseWriter.Write(syntaxReader.ReadBytes(32));
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadBytes(4));
                    reverseWriter.Write(syntaxReader.ReadInt32());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    short maxNum = syntaxReader.ReadInt16();
                    reverseWriter.Write(maxNum);
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadInt16());
                    reverseWriter.Write(syntaxReader.ReadUInt32());

                    for (int i = 0; i < maxNum; i++)
                    {
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt16());
                        reverseWriter.Write(syntaxReader.ReadInt32());
                        reverseWriter.Write(syntaxReader.ReadInt32());
                        reverseWriter.Write(syntaxReader.ReadInt32());
                    }

                    reverseStream.Position = 0;
                    syntaxData.data = reverseStream.ToArray();
                    values.SetValue(syntaxData, xpe.IndexByName("script syntax data"));
                    return;
                default:
                    return;
            }
        }

        private object RecursiveReadMap(Xpe.Field field, TagLibrary lib, int absolute, uint magic)
        {
            switch (field.Type)
            {
                case Xpe.FieldType.TagReference:
                    er.Position = absolute + field.Offset + 12;
                    uint id = er.ReadUInt32();
                    if (id == 0xffffffff)
                        return -1;
                    else
                    {
                        int tagNumber = lib.IndexByID(id);
                        //if (lib[tagNumber].isprotected)
                        //{
                        //    int hashNum = lib[tagNumber].CheckHash(lib, lib.CXPE);
                        //    if (hashNum != -1)
                        //    {
                        //        string tagName =  lib.CXPE.md5Name[hashNum].ToString();
                        //        lib.Refactor(tagNumber, tagName);
                        //        lib[tagNumber] = new Tag(lib.CXPE.XBdir + "\\" + tagName + "." + this.xpe.Extension , lib.CXPE.XBdir, lib, lib.CXPE.CEdir);
                        //    }
                        //}
                        return tagNumber;
                    }
                case Xpe.FieldType.Block:
                    Xpe.Field[] children = field.Children;
                    er.Position = absolute + field.Offset;
                    int count = er.ReadInt32();
                    if (count == 0)
                        return Array.CreateInstance(typeof(object), 0, children.Length);
                    uint pOffset = er.ReadUInt32() - magic;
                    Array values = Array.CreateInstance(typeof(object), count, children.Length);
                    for (int x = 0; x < count; x++)
                        for (int y = 0; y < children.Length; y++)
                            values.SetValue(RecursiveReadMap(children[y], lib, (int)(pOffset + (x * field.Size)), magic), x, y);
                    return values;

                case Xpe.FieldType.BitmapDataBlock:
                    if (typeCounter == 0)
                    {
                        typeCounter = er.ReadInt32();
                        reservedTypeData = HaloMap.Round(er.ReadInt32(), 0x200);
                        return new int[] { typeCounter, (int)reservedTypeData, 0 };
                    }
                    else
                    {
                        int offset = er.ReadInt32();
                        int size = HaloMap.Round(er.ReadInt32(), 0x200);
                        int ret = (int)reservedTypeData; //er.ReadInt32() - typeCounter;
                        reservedTypeData = (int)reservedTypeData + size;

                        return new int[] { offset, size, ret };
                    }
                //if (typeCounter == 0)
                //{
                //    typeCounter = er.ReadInt32();
                //    reservedTypeData = er.ReadInt32();
                //    return 0;
                //}
                //else
                //{
                //    int ret = er.ReadInt32() - typeCounter;
                //    reservedTypeData = (int)reservedTypeData + HaloMap.Round(er.ReadInt32(), 0x200);
                //    return ret;
                //}
                case Xpe.FieldType.BspUncompressedVertexBlock:
                    if (lib.MapFormat != HaloMap.MapFormat.Xbox)
                    {
                        er.Position = absolute + field.Offset;
                        int byteCount = er.ReadInt32();
                        er.Position += 4;
                        int dword = er.ReadInt32(); // TODO: Figure out what this value is for
                        er.Position = (int)(er.ReadUInt32() - magic);
                        return new DataContainer(dword, er.ReadBytes(byteCount));
                    }
                    else
                    {
                        er.Position = absolute + field.Offset + 8;
                        return new DataContainer(er.ReadInt32(), new byte[0]);
                    }

                case Xpe.FieldType.BspCompressedVertexBlock:
                    if (lib.MapFormat == HaloMap.MapFormat.Xbox)
                    {
                        er.Position = absolute + field.Offset;
                        int byteCount = er.ReadInt32();
                        er.Position += 4;
                        int dword = er.ReadInt32(); // TODO: Figure out what this value is for
                        er.Position = (int)(er.ReadUInt32() - magic);
                        return new DataContainer(dword, er.ReadBytes(byteCount));
                    }
                    else
                    {
                        er.Position = absolute + field.Offset + 8;
                        return new DataContainer(er.ReadInt32(), new byte[0]);
                    }

                case Xpe.FieldType.ScriptData:
                    //er.Position = absolute + field.Offset;
                    //int sbyteCount = er.ReadInt32();
                    //if (sbyteCount == 0)
                    //    return new DataContainer(0, new byte[0]);
                    //er.Position += 8;
                    //uint bsOffset = er.ReadUInt32() - magic;
                    //er.Position = (int)bsOffset;

                    //return new DataContainer(sbyteCount, er.ReadBytes(HaloMap.Round(sbyteCount, 4)));
                case Xpe.FieldType.Prestructure:
                    er.Position = absolute + field.Offset;
                    int psbyteCount = er.ReadInt32();
                    if (psbyteCount == 0)
                        return new DataContainer(0, new byte[0]);
                    er.Position += 8;
                    uint bOffset = er.ReadUInt32() - magic;
                    er.Position = (int)bOffset;

                    return new DataContainer(psbyteCount, er.ReadBytes(psbyteCount));


                case Xpe.FieldType.ModelIndexBlock:
                    ArrayList indexList = reservedTypeData as ArrayList;
                    er.Position = absolute + field.Offset;
                    int triCount = er.ReadInt32();
                    if (triCount == 0)
                        return new byte[0];
                    uint triOffset = (uint)((long)er.ReadUInt32() - (lib.MapFormat == HaloMap.MapFormat.Xbox ? (long)magic : -(long)(lib.ModelIndexOffset + lib.ModelVertexOffset)));
                    triCount += 1;
                    er.Position = (int)triOffset + triCount * 2;
                    int remainder = triCount % 3;
                    triCount += (3 - remainder);
                    //if (er.ReadUInt16() == 0xffff)
                    //    triCount += 3;
                    //else if (er.ReadUInt16() == 0xffff)
                    //    triCount += 3;
                    //else if (er.ReadUInt16() == 0xffff)
                    //    triCount += 3;
                    er.Position = (int)triOffset;
                    byte[] tris = er.ReadBytes(triCount * 2);
                    Array triangles = Array.CreateInstance(typeof(short), triCount / 3, 3);
                    for (int x = 0; x < triCount / 3; x++)
                    {
                        triangles.SetValue(BitConverter.ToInt16(tris, x * 6), x, 0);
                        triangles.SetValue(BitConverter.ToInt16(tris, x * 6 + 2), x, 1);
                        triangles.SetValue(BitConverter.ToInt16(tris, x * 6 + 4), x, 2);
                    }
                    indexList.Add(triangles);
                    return new byte[0];

                case Xpe.FieldType.ModelVertexBlock:
                    ArrayList vertexList = reservedTypeData as ArrayList;
                    er.Position = absolute + field.Offset;
                    int vertCount = er.ReadInt32();
                    if (vertCount == 0)
                        return new byte[0];
                    er.Position = absolute + field.Offset + 12; // hack alert lolz
                    uint vertOffset = (uint)((long)er.ReadUInt32() - (lib.MapFormat == HaloMap.MapFormat.Xbox ? (long)magic : -(long)lib.ModelVertexOffset));
                    if (lib.MapFormat == HaloMap.MapFormat.Xbox)
                    {
                        er.Position = (int)vertOffset + 4;
                        vertOffset = (uint)(er.ReadInt32() - magic);
                    }
                    er.Position = (int)vertOffset;
                    byte[] verts = er.ReadBytes(vertCount * (lib.MapFormat == HaloMap.MapFormat.Xbox ? 32 : 68));
                    Array vertices = Array.CreateInstance(typeof(object), vertCount, 9);
                    if (lib.MapFormat == HaloMap.MapFormat.Xbox)
                    {
                        for (int x = 0; x < vertCount; x++)
                        {
                            Array vPos = Array.CreateInstance(typeof(float), 3);
                            vPos.SetValue(BitConverter.ToSingle(verts, x * 32), 0);
                            vPos.SetValue(BitConverter.ToSingle(verts, x * 32 + 4), 1);
                            vPos.SetValue(BitConverter.ToSingle(verts, x * 32 + 8), 2);
                            vertices.SetValue(vPos, x, 0);
                            vertices.SetValue(BitConverter.ToInt32(verts, x * 32 + 12), x, 1);
                            vertices.SetValue(BitConverter.ToInt32(verts, x * 32 + 16), x, 2);
                            vertices.SetValue(BitConverter.ToInt32(verts, x * 32 + 20), x, 3);
                            vertices.SetValue(BitConverter.ToInt16(verts, x * 32 + 24), x, 4);
                            vertices.SetValue(BitConverter.ToInt16(verts, x * 32 + 26), x, 5);
                            vertices.SetValue(verts[x * 32 + 28], x, 6);
                            vertices.SetValue(verts[x * 32 + 29], x, 7);
                            vertices.SetValue(BitConverter.ToInt16(verts, x * 32 + 30), x, 8);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < vertCount; x++)
                        {
                            // Position
                            Array vPos = Array.CreateInstance(typeof(float), 3);
                            vPos.SetValue(BitConverter.ToSingle(verts, x * 68), 0);
                            vPos.SetValue(BitConverter.ToSingle(verts, x * 68 + 4), 1);
                            vPos.SetValue(BitConverter.ToSingle(verts, x * 68 + 8), 2);
                            vertices.SetValue(vPos, x, 0);
                            // Normal
                            Array vNormal = Array.CreateInstance(typeof(float), 3);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68) + 12, 0);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68 + 16), 1);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68 + 20), 2);
                            vertices.SetValue(HaloMap.Compress111110(vNormal), x, 1);
                            // BiNormal
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68) + 24, 0);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68 + 28), 1);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68 + 32), 2);
                            vertices.SetValue(HaloMap.Compress111110(vNormal), x, 2);
                            // Tangent
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68) + 36, 0);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68 + 40), 1);
                            vNormal.SetValue(BitConverter.ToSingle(verts, x * 68 + 44), 2);
                            vertices.SetValue(HaloMap.Compress111110(vNormal), x, 3);
                            // Texcoords
                            vertices.SetValue(HaloMap.CompressHalf16(BitConverter.ToSingle(verts, x * 68 + 48)), x, 4);
                            vertices.SetValue(HaloMap.CompressHalf16(BitConverter.ToSingle(verts, x * 68 + 52)), x, 5);
                            // Node indices
                            vertices.SetValue((byte)(BitConverter.ToInt16(verts, x * 68 + 56) * 3), x, 6);
                            vertices.SetValue((byte)(BitConverter.ToInt16(verts, x * 68 + 58) * 3), x, 7);
                            // Node weight
                            vertices.SetValue(HaloMap.CompressHalf16(BitConverter.ToSingle(verts, x * 68 + 60)), x, 8);
                        }
                    }
                    vertexList.Add(vertices);
                    return new byte[0];

                case Xpe.FieldType.BitmapPixelData: // This needs to be taken care of AFTER we go through the bitmap blocks
                    return new byte[0];

                case Xpe.FieldType.SoundDataBlock:
                    er.Position = absolute + field.Offset;
                    int dataLen = er.ReadInt32();
                    if (dataLen == 0)
                        return new byte[0];
                    er.Position += 4;
                    int dOffset = er.ReadInt32();
                    er.Position = dOffset;
                    return er.ReadBytes(dataLen);

                case Xpe.FieldType.Char:
                case Xpe.FieldType.CharFlags:
                    er.Position = absolute + field.Offset;
                    return er.ReadByte();

                case Xpe.FieldType.DWordFlags:
                case Xpe.FieldType.Int1:
                    er.Position = absolute + field.Offset;
                    return er.ReadInt32();

                case Xpe.FieldType.WordFlags:
                case Xpe.FieldType.Enum:
                case Xpe.FieldType.Short1:
                    er.Position = absolute + field.Offset;
                    return er.ReadInt16();

                case Xpe.FieldType.Float1:
                    er.Position = absolute + field.Offset;
                    return er.ReadSingle();

                case Xpe.FieldType.Float2:
                    er.Position = absolute + field.Offset;
                    return new float[] { er.ReadSingle(), er.ReadSingle() };

                case Xpe.FieldType.Float3:
                    er.Position = absolute + field.Offset;
                    return new float[] { er.ReadSingle(), er.ReadSingle(), er.ReadSingle() };

                case Xpe.FieldType.Float4:
                    er.Position = absolute + field.Offset;
                    return new float[] { er.ReadSingle(), er.ReadSingle(), er.ReadSingle(), er.ReadSingle() };

                case Xpe.FieldType.Int2:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32() };

                case Xpe.FieldType.Int3:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32(), er.ReadInt32() };

                case Xpe.FieldType.Int4:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32(), er.ReadInt32(), er.ReadInt32() };

                case Xpe.FieldType.Short2:
                    er.Position = absolute + field.Offset;
                    return new short[] { er.ReadInt16(), er.ReadInt16() };

                case Xpe.FieldType.Short3:
                    er.Position = absolute + field.Offset;
                    return new short[] { er.ReadInt16(), er.ReadInt16(), er.ReadInt16() };

                case Xpe.FieldType.Short4:
                    er.Position = absolute + field.Offset;
                    return new short[] { er.ReadInt16(), er.ReadInt16(), er.ReadInt16(), er.ReadInt16() };

                case Xpe.FieldType.String:
                    er.Position = absolute + field.Offset;
                    string test = er.ReadString(32);
                    return test;

                case Xpe.FieldType.Padding:
                    er.Position = absolute + field.Offset;
                    return er.ReadBytes(field.Size);
                case Xpe.FieldType.BspVertexDataHeader:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32(), er.ReadInt32(), er.ReadInt32() };
                default:
                    return null;
            }
        }

        private object RecursiveReadFile(Xpe.Field field, TagLibrary lib, string directory, int absolute, ref int absoluteLastEnd, string ceTagsDirectory, bool tagHash)
        {
            switch (field.Type)
            {
                case Xpe.FieldType.TagReference:
                    er.Position = absolute + field.Offset;
                    string type = er.ReadFourCC();
                    er.Position += 4;
                    int strLen = er.ReadInt32();
                    if (strLen == 0)
                        return -1;
                    er.Position = absoluteLastEnd;
                    string name = er.ReadString(strLen);
                    absoluteLastEnd += strLen + 1;
                    if (tagHash)
                    {
                        return lib.GetDependencyTag(name, type, directory, ceTagsDirectory);
                    }
                    else
                    { return lib.GetDependencyTag(name, type, directory, ceTagsDirectory); }

                case Xpe.FieldType.Block:
                    er.Position = absolute + field.Offset;
                    int count = er.ReadInt32();
                    int prevEnd = absoluteLastEnd;
                    absoluteLastEnd += count * field.Size;
                    Xpe.Field[] children = field.Children;
                    Array values = Array.CreateInstance(typeof(object), count, children.Length);
                    for (int x = 0; x < count; x++)
                        for (int y = 0; y < children.Length; y++)
                            values.SetValue(RecursiveReadFile(children[y], lib, directory, prevEnd + (x * field.Size), ref absoluteLastEnd, ceTagsDirectory, tagHash), x, y);
                    return values;

                case Xpe.FieldType.BitmapPixelData:
                case Xpe.FieldType.ModelIndexBlock:
                case Xpe.FieldType.ModelVertexBlock:
                case Xpe.FieldType.SoundDataBlock:
                    er.Position = absolute + field.Offset;
                    int byteCount = er.ReadInt32();
                    er.Position = absoluteLastEnd;
                    absoluteLastEnd += byteCount;
                    return er.ReadBytes(byteCount);

                case Xpe.FieldType.ScriptData:
                    //er.Position = absolute + field.Offset;
                    //int tsbyteCount = er.ReadInt32();
                    //er.Position = absoluteLastEnd;
                    //absoluteLastEnd += HaloMap.Round(tsbyteCount, 4);
                    //return new DataContainer(tsbyteCount, er.ReadBytes(HaloMap.Round(tsbyteCount, 4)));
                case Xpe.FieldType.Prestructure:
                    er.Position = absolute + field.Offset;
                    int tbyteCount = er.ReadInt32();
                    er.Position = absoluteLastEnd;
                    absoluteLastEnd += tbyteCount;
                    return new DataContainer(tbyteCount, er.ReadBytes(tbyteCount));

                case Xpe.FieldType.BspCompressedVertexBlock:
                case Xpe.FieldType.BspUncompressedVertexBlock:
                    er.Position = absolute + field.Offset;
                    int dbyteCount = er.ReadInt32();
                    er.Position += 4;
                    int dword = er.ReadInt32(); // TODO: Figure out what this value is for
                    er.Position = absoluteLastEnd;
                    absoluteLastEnd += dbyteCount;
                    return new DataContainer(dword, er.ReadBytes(dbyteCount));



                case Xpe.FieldType.Char:
                case Xpe.FieldType.CharFlags:
                    er.Position = absolute + field.Offset;
                    return er.ReadByte();

                case Xpe.FieldType.DWordFlags:
                case Xpe.FieldType.Int1:
                    er.Position = absolute + field.Offset;
                    return er.ReadInt32();

                case Xpe.FieldType.WordFlags:
                case Xpe.FieldType.Enum:
                case Xpe.FieldType.Short1:
                    er.Position = absolute + field.Offset;
                    return er.ReadInt16();

                case Xpe.FieldType.Float1:
                    er.Position = absolute + field.Offset;
                    return er.ReadSingle();

                case Xpe.FieldType.Float2:
                    er.Position = absolute + field.Offset;
                    return new float[] { er.ReadSingle(), er.ReadSingle() };

                case Xpe.FieldType.Float3:
                    er.Position = absolute + field.Offset;
                    return new float[] { er.ReadSingle(), er.ReadSingle(), er.ReadSingle() };

                case Xpe.FieldType.Float4:
                    er.Position = absolute + field.Offset;
                    return new float[] { er.ReadSingle(), er.ReadSingle(), er.ReadSingle(), er.ReadSingle() };

                case Xpe.FieldType.BitmapDataBlock:
                    return er.ReadInt32();

                case Xpe.FieldType.Int2:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32() };

                case Xpe.FieldType.Int3:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32(), er.ReadInt32() };

                case Xpe.FieldType.Int4:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32(), er.ReadInt32(), er.ReadInt32() };

                case Xpe.FieldType.Short2:
                    er.Position = absolute + field.Offset;
                    return new short[] { er.ReadInt16(), er.ReadInt16() };

                case Xpe.FieldType.Short3:
                    er.Position = absolute + field.Offset;
                    return new short[] { er.ReadInt16(), er.ReadInt16(), er.ReadInt16() };

                case Xpe.FieldType.Short4:
                    er.Position = absolute + field.Offset;
                    return new short[] { er.ReadInt16(), er.ReadInt16(), er.ReadInt16(), er.ReadInt16() };

                case Xpe.FieldType.String:
                    er.Position = absolute + field.Offset;
                    string tempStr = er.ReadString(32);
                    //int nullIndex = tempStr.IndexOf('\0');
                    //if (nullIndex > -1)
                    //    return tempStr.Substring(0, nullIndex);
                    //else
                    return tempStr;

                case Xpe.FieldType.Padding:
                    er.Position = absolute + field.Offset;
                    return er.ReadBytes(field.Size);
                case Xpe.FieldType.BspVertexDataHeader:
                    er.Position = absolute + field.Offset;
                    return new int[] { er.ReadInt32(), er.ReadInt32(), er.ReadInt32(), er.ReadInt32() };
                default:
                    return null;
            }
        }

        private void RecursiveWriteFile(EndianWriter ew, Xpe.Field field, Array vset, int ycount, int index, TagLibrary lib, int absolute, ref uint absoluteEnd, bool tagHash)
        {

            ew.Position = absolute + field.Offset;

            object value = null;
            if (ycount < 0)
                value = vset.GetValue(index);
            else
            {
                if (index == 15 && vset.GetLength(1) == 15 && field.Name == "shorten me" && field.Type == Xpe.FieldType.Padding)
                {
                    value = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                }
                else
                {
                    value = vset.GetValue(ycount, index);
                }
            }


            Array array = value as Array;

            switch (field.Type)
            {
                case Xpe.FieldType.BitmapDataBlock:
                    ew.Write(Convert.ToInt32(value));
                    ew.Write(0);
                    break;

                case Xpe.FieldType.SoundDataBlock:
                case Xpe.FieldType.BitmapPixelData:
                    int pDataLen = array.GetLength(0);
                    ew.Write(pDataLen);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Position = (int)absoluteEnd;
                    absoluteEnd += (uint)pDataLen;
                    ew.Write(array as byte[]);
                    break;
                case Xpe.FieldType.ScriptData:
                    //int pdDataLen = (value as DataContainer).variable;
                    //ew.Write(pdDataLen);
                    //ew.Write(0);
                    //ew.Write(0);
                    //ew.Write(0);
                    //ew.Write(0);
                    //ew.Position = (int)absoluteEnd;
                    //absoluteEnd += (uint)HaloMap.Round(pdDataLen, 4);
                    //ew.Write((value as DataContainer).data);
                    //break;
                case Xpe.FieldType.Prestructure:
                    int pdDataLen2 = (value as DataContainer).variable;
                    ew.Write(pdDataLen2);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Position = (int)absoluteEnd;
                    absoluteEnd += (uint)pdDataLen2;
                    ew.Write((value as DataContainer).data);
                    break;
                case Xpe.FieldType.Block:
                    Array nvset = value as Array;
                    int count = nvset.GetLength(0);
                    if (field.Name == "predicted resources" || field.Name == "PREDICTED RESOURCES")
                        count = 0; // lol fuck these things
                    if (count == 0)
                    {
                        ew.Write(0);
                        ew.Write(0);
                        ew.Write(0);
                        break;
                    }
                    ew.Write(count);
                    ew.Write(0);
                    ew.Write(0);
                    int nAbsolute = (int)absoluteEnd;
                    absoluteEnd += (uint)(field.Size * count);
                    ew.Length += field.Size * count;
                    int cCount = field.Children.Length;
                    for (int x = 0; x < count; x++)
                        for (int y = 0; y < cCount; y++)
                            RecursiveWriteFile(ew, field.Children[y], nvset, x, y, lib, nAbsolute + (x * field.Size), ref absoluteEnd, tagHash);
                    break;
                case Xpe.FieldType.BspCompressedVertexBlock:
                    DataContainer dc = value as DataContainer;
                    int dpDataLen = dc.data.GetLength(0);
                    ew.Write(dpDataLen);
                    ew.Write(0);
                    ew.Write(dc.variable);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Position = (int)absoluteEnd;
                    absoluteEnd += (uint)dpDataLen;
                    ew.Write(dc.data);
                    break;
                case Xpe.FieldType.BspUncompressedVertexBlock:
                    DataContainer udc = value as DataContainer;
                    int udpDataLen = udc.data.GetLength(0);
                    ew.Write(udpDataLen);
                    ew.Write(0);
                    ew.Write(udc.variable);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Position = (int)absoluteEnd;
                    absoluteEnd += (uint)udpDataLen;
                    ew.Write(udc.data);
                    break;
                case Xpe.FieldType.BspVertexDataHeader:
                    //ew.Write(0);

                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    ew.Write(Convert.ToInt32(array.GetValue(3)));
                    //ew.Write(Convert.ToInt32(array.GetValue(4)));
                    //ew.Write(0);
                    //ew.Write(0);
                    //ew.Write(0);
                    break;
                case Xpe.FieldType.Char:
                case Xpe.FieldType.CharFlags:
                    ew.Write(Convert.ToByte(value));
                    break;
                case Xpe.FieldType.Float1:
                    ew.Write(Convert.ToSingle(value));
                    break;
                case Xpe.FieldType.Float2:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Float3:
                    if (value is Array)
                    {
                        ew.Write(Convert.ToSingle(array.GetValue(0)));
                        ew.Write(Convert.ToSingle(array.GetValue(1)));
                        ew.Write(Convert.ToSingle(array.GetValue(2)));
                    }
                    else
                    {
                        ew.Write(0);
                        ew.Write(0);
                        ew.Write(0);
                    }
                    break;
                case Xpe.FieldType.Float4:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    ew.Write(Convert.ToSingle(array.GetValue(2)));
                    ew.Write(Convert.ToSingle(array.GetValue(3)));
                    break;
                case Xpe.FieldType.DWordFlags:
                case Xpe.FieldType.Int1:
                    if (value is int)
                        ew.Write(Convert.ToInt32(value));
                    else if (value is uint)
                        ew.Write(Convert.ToUInt32(value));
                    else if (value is short)
                        ew.Write((int)Convert.ToInt16(value));
                    else if (value is ushort)
                        ew.Write((uint)Convert.ToUInt16(value));
                    else
                        throw new Exception("Unknown object type given for Int1/DWordFlags.");
                    break;
                case Xpe.FieldType.Int2:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Int3:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Int4:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    ew.Write(Convert.ToInt32(array.GetValue(3)));
                    break;
                case Xpe.FieldType.ModelIndexBlock:
                case Xpe.FieldType.ModelVertexBlock:
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.Padding:
                    ew.Write(array as byte[]);
                    break;
                case Xpe.FieldType.SelfReference:
                    if (tagHash)
                    { ew.Write((uint)0); }
                    else
                    { ew.Write((uint)0); }//lib.IDByTag(this)); }
                    break;
                case Xpe.FieldType.Enum:
                case Xpe.FieldType.WordFlags:
                case Xpe.FieldType.Short1:
                    ew.Write(Convert.ToInt16(value));
                    break;
                case Xpe.FieldType.Short2:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Short3:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    ew.Write(Convert.ToInt16(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Short4:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    ew.Write(Convert.ToInt16(array.GetValue(2)));
                    ew.Write(Convert.ToInt16(array.GetValue(3)));
                    break;
                case Xpe.FieldType.String:
                    ew.Write(Convert.ToString(value), false);
                    break;
                case Xpe.FieldType.TagReference:
                    int tag = Convert.ToInt32(value);
                    if (tagHash)
                    {
                        if (tag < 0)
                        {
                            if (field.Arguments[0] == "mod2")
                                ew.WriteFourCC("mode");
                            else
                                ew.WriteFourCC(field.Arguments[0]);
                            ew.Write(0);
                            ew.Write(0);
                            ew.Write(-1);
                        }
                        else
                        {
                            if (lib[tag] == null)
                            {
                                ew.Write(-1);
                                ew.Write(-1);
                                ew.Write(-1);
                                ew.Write(-1);
                            }
                            else
                            {
                                if (lib[tag].Xpe.FourCC == "snd!" || lib[tag].Xpe.FourCC == "bitm" || lib.CXPE.RootTag == tag)
                                {
                                    ew.Write(-1);
                                    ew.Write(-1);
                                    ew.Write(-1);
                                    ew.Write(-1);
                                }
                                else
                                {
                                    if (!lib.CXPE.HashExists(lib[tag].Name))
                                    {
                                        // Console.WriteLine("Calling " + Name + " Called " + lib[tag].Name + " New");
                                        byte[] hash = lib[tag].ReturnHash(lib);
                                        lib.CXPE.AddHash(lib[tag].Name, hash);
                                        ew.Write(hash);

                                    }
                                    else
                                    {
                                        // Console.WriteLine("Calling " + Name + " Called " + lib[tag].Name + " Old");
                                        ew.Write(lib.CXPE.GetHash(lib[tag].Name));
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        if (tag < 0)
                        {
                            if (field.Arguments[0] == "mod2")
                                ew.WriteFourCC("mode");
                            else
                                ew.WriteFourCC(field.Arguments[0]);
                            ew.Write(0);
                            ew.Write(0);
                            ew.Write(-1);
                        }
                        else
                        {
                            string tType = lib.GetClass(tag);
                            string tName = lib.GetName(tag);

                            if (tType == "mod2")
                                ew.WriteFourCC("mode");
                            else
                                ew.WriteFourCC(tType);
                            ew.Write(0);
                            int increment = tName.TrimEnd(new char[] { '\0' }).Length;
                            ew.Write(increment);
                            ew.Write(-1);
                            ew.Position = (int)absoluteEnd;
                            absoluteEnd += (uint)(increment + 1);
                            ew.Write(tName, true);
                        }
                    }
                    break;
            }
            ew.Flush();
        }

        private void RecursiveWriteMap(EndianWriter ew, Xpe.Field field, Array vset, int ycount, int index, TagLibrary lib, int absolute, ref uint absoluteEnd, int[] aux, uint memory)
        {
            ew.Position = absolute + field.Offset;
            object value = null;
            if (ycount < 0)
                value = vset.GetValue(index);
            else
                value = vset.GetValue(ycount, index);
            Array array = value as Array;

            switch (field.Type)
            {
                case Xpe.FieldType.BitmapDataBlock:
                    ew.Write(Convert.ToInt32(value) + aux[0]);
                    ew.Write(CalculateBitmapSize((short)vset.GetValue(ycount, 1), (short)vset.GetValue(ycount, 2), (short)vset.GetValue(ycount, 3), (short)vset.GetValue(ycount, 4), (short)vset.GetValue(ycount, 5), (short)vset.GetValue(ycount, 8)));
                    break;
                case Xpe.FieldType.BitmapPixelData:
                    ew.Write(0);
                    ew.Write(0);
                    //if(field.Name == "compressed color plate data")
                    //	ew.Write(0x6c); // TODO: find out what the hell this is
                    //else
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.Block:
                    Array nvset = value as Array;
                    int count = nvset.GetLength(0);
                    if (xpe.FourCC.StartsWith("mod"))
                    {
                        if (field.Name == "uncompressed vertices")
                            count = 0;
                        if (field.Name == "compressed vertices")
                            count = 0;
                        if (field.Name == "triangles")
                            count = 0;
                    }
                    if (count == 0)
                    {
                        ew.Write(0);
                        ew.Write(0);
                        ew.Write(0);
                        break;
                    }
                    ew.Write(count);
                    ew.Write(absoluteEnd + memory);
                    ew.Write(0);
                    int nAbsolute = (int)absoluteEnd;
                    absoluteEnd += (uint)HaloMap.Round(field.Size * count, 4);
                    ew.Length += HaloMap.Round(field.Size * count, 4);
                    int cCount = field.Children.Length;
                    for (int x = 0; x < count; x++)
                        for (int y = 0; y < cCount; y++)
                            RecursiveWriteMap(ew, field.Children[y], nvset, x, y, lib, nAbsolute + (x * field.Size), ref absoluteEnd, aux, memory);
                    break;
                case Xpe.FieldType.BspCompressedVertexBlock:
                    ew.Write(aux[typeCounter * 6 + 1]);
                    ew.Write(0);
                    ew.Write((value as DataContainer).variable);
                    ew.Write((uint)aux[2 + ((typeCounter++) * 6)] + memory);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.BspUncompressedVertexBlock:
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write((value as DataContainer).variable);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.BspVertexDataHeader:
                    if (field.Name == "vertex buffer")
                        ew.Write(1);
                    else if (field.Name == "lightmap buffer")
                        ew.Write(3);
                    if (field.Name == "vertex buffer")
                        ew.Write(aux[typeCounter * 6]);
                    else if (field.Name == "lightmap buffer")
                        ew.Write(aux[typeCounter * 6 + 3]);
                    ew.Write(0);
                    ew.Write(0);
                    if (field.Name == "vertex buffer")
                        ew.Write(aux[typeCounter * 6 + 4]);
                    else if (field.Name == "lightmap buffer")
                        ew.Write(aux[typeCounter * 6 + 5]);
                    break;
                case Xpe.FieldType.Char:
                case Xpe.FieldType.CharFlags:
                    ew.Write(Convert.ToByte(value));
                    break;
                case Xpe.FieldType.Float1:
                    ew.Write(Convert.ToSingle(value));
                    break;
                case Xpe.FieldType.Float2:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Float3:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    ew.Write(Convert.ToSingle(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Float4:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    ew.Write(Convert.ToSingle(array.GetValue(2)));
                    ew.Write(Convert.ToSingle(array.GetValue(3)));
                    break;
                case Xpe.FieldType.DWordFlags:
                case Xpe.FieldType.Int1:
                    if (value is int)
                        ew.Write(Convert.ToInt32(value));
                    else if (value is uint)
                        ew.Write(Convert.ToUInt32(value));
                    else if (value is short)
                        ew.Write((int)Convert.ToInt16(value));
                    else if (value is ushort)
                        ew.Write((uint)Convert.ToUInt16(value));
                    else
                        throw new Exception("Unknown object type given for Int1/DWordFlags.");
                    break;
                case Xpe.FieldType.Int2:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Int3:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Int4:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    ew.Write(Convert.ToInt32(array.GetValue(3)));
                    break;
                case Xpe.FieldType.ModelIndexBlock:
                    ew.Write(aux[typeCounter * 5]);
                    ew.Write(aux[1 + (typeCounter * 5)]);
                    ew.Write(aux[4 + (typeCounter * 5)]);
                    ew.Write(5); // TODO: Figure out what the hell this value is
                    break;
                case Xpe.FieldType.ModelVertexBlock:
                    ew.Write(aux[2 + typeCounter * 5]);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(aux[3 + ((typeCounter++) * 5)]);
                    break;
                case Xpe.FieldType.Padding:
                    if (field.Name == "shader type index")
                    {
                        array.SetValue((byte)0x00, 1);
                        switch (xpe.FourCC)
                        {
                            case "senv":
                                array.SetValue((byte)0x03, 0);
                                break;
                            case "sgla":
                                array.SetValue((byte)0x08, 0);
                                break;
                            case "swat":
                                array.SetValue((byte)0x07, 0);
                                break;
                            case "sotr":
                                array.SetValue((byte)0x05, 0);
                                break;
                            case "soso":
                                array.SetValue((byte)0x04, 0);
                                break;
                            case "schi":
                            case "scex":
                                array.SetValue((byte)0x06, 0);
                                break;
                            case "spla":
                                array.SetValue((byte)0x0a, 0);
                                break;
                            case "smet":
                                array.SetValue((byte)0x09, 0);
                                break;
                        }
                    }
                    ew.Write(array as byte[]);
                    break;
                case Xpe.FieldType.ScriptData:
                    //int hscCount = (value as DataContainer).variable;
                    //ew.Write(hscCount);
                    //ew.Write(0);
                    //ew.Write(0);
                    //ew.Write(absoluteEnd + memory);
                    //ew.Write(0);
                    //ew.Position = (int)absoluteEnd;
                    //absoluteEnd += (uint)HaloMap.Round(hscCount, 4);
                    //ew.Write((value as DataContainer).data);
                    //break;

                case Xpe.FieldType.Prestructure:
                    int hscpCount = (value as DataContainer).variable;
                    ew.Write(hscpCount);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(absoluteEnd + memory);
                    ew.Write(0);
                    ew.Position = (int)absoluteEnd;
                    absoluteEnd += (uint)hscpCount;
                    ew.Write((value as DataContainer).data);
                    break;

                case Xpe.FieldType.SelfReference:
                    ew.Write(lib.IDByTag(this));
                    break;
                case Xpe.FieldType.Enum:
                case Xpe.FieldType.WordFlags:
                case Xpe.FieldType.Short1:
                    ew.Write(Convert.ToInt16(value));
                    break;
                case Xpe.FieldType.Short2:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Short3:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    ew.Write(Convert.ToInt16(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Short4:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    ew.Write(Convert.ToInt16(array.GetValue(2)));
                    ew.Write(Convert.ToInt16(array.GetValue(3)));
                    break;
                case Xpe.FieldType.SoundDataBlock:
                    ew.Write(aux[typeCounter * 2]);
                    ew.Write(0);
                    ew.Write(aux[1 + ((typeCounter++) * 2)]);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.String:
                    ew.Write(Convert.ToString(value), false);
                    break;
                case Xpe.FieldType.TagReference:
                    int tag = Convert.ToInt32(value);
                    if (tag < 0)
                    {
                        if (field.Arguments[0] == "mod2")
                            ew.WriteFourCC("mode");
                        else
                            ew.WriteFourCC(field.Arguments[0]);
                        ew.Write(0);
                        ew.Write(0);
                        ew.Write(-1);
                    }
                    else
                    {
                        string tType = lib.GetClass(tag);
                        if (tType == "mod2")
                            ew.WriteFourCC("mode");
                        else
                            ew.WriteFourCC(tType);
                        ew.Write(lib.StringOffsets[tag]);
                        ew.Write(0);
                        ew.Write(lib.GetID(tag));
                    }
                    break;
            }
            ew.Flush();
        }

        private void RecursiveWriteMap(EndianWriter ew, Xpe.Field field, Array vset, int ycount, int index, TagLibrary lib, int absolute, ref uint absoluteEnd, int[] aux, ArrayList pointers, ArrayList destinations)
        {
            ew.Position = absolute + field.Offset;
            object value = null;
            if (ycount < 0)
                value = vset.GetValue(index);
            else
                value = vset.GetValue(ycount, index);
            Array array = value as Array;
            switch (field.Type)
            {
                case Xpe.FieldType.BitmapDataBlock:
                    ew.Write(Convert.ToInt32(value) + aux[typeCounter++]);
                    ew.Write(CalculateBitmapSize((short)vset.GetValue(ycount, 1), (short)vset.GetValue(ycount, 2), (short)vset.GetValue(ycount, 3), (short)vset.GetValue(ycount, 4), (short)vset.GetValue(ycount, 5), (short)vset.GetValue(ycount, 8)));
                    break;
                case Xpe.FieldType.BitmapPixelData:
                    ew.Write(0);
                    ew.Write(0);
                    //if(field.Name == "compressed color plate data")
                    //	ew.Write(0x6c); // TODO: find out what the hell this is
                    //else
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.Block:
                    Array nvset = value as Array;
                    int count = nvset.GetLength(0);
                    if (xpe.FourCC.StartsWith("mod"))
                    {
                        if (field.Name == "uncompressed vertices")
                            count = 0;
                        if (field.Name == "compressed vertices")
                            count = 0;
                        if (field.Name == "triangles")
                            count = 0;
                    }
                    if (count == 0)
                    {
                        ew.Write(0);
                        ew.Write(0);
                        ew.Write(0);
                        break;
                    }
                    ew.Write(count);
                    pointers.Add(ew.Position);
                    ew.Write(0);
                    ew.Write(0);
                    destinations.Add(absoluteEnd);
                    int nAbsolute = (int)absoluteEnd;
                    absoluteEnd += (uint)HaloMap.Round(field.Size * count, 4);
                    ew.Length += HaloMap.Round(field.Size * count, 4);
                    int cCount = field.Children.Length;
                    for (int x = 0; x < count; x++)
                        for (int y = 0; y < cCount; y++)
                            RecursiveWriteMap(ew, field.Children[y], nvset, x, y, lib, nAbsolute + (x * field.Size), ref absoluteEnd, aux, pointers, destinations);
                    break;
                case Xpe.FieldType.BspCompressedVertexBlock:
                    ew.Write(aux[typeCounter * 6 + 1]);
                    ew.Write(0);
                    ew.Write((value as DataContainer).variable);
                    pointers.Add(ew.Position);
                    destinations.Add((uint)aux[2 + ((typeCounter++) * 6)]);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.BspUncompressedVertexBlock:
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write((value as DataContainer).variable);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.BspVertexDataHeader:
                    if (field.Name == "vertex buffer")
                        ew.Write(1);
                    else if (field.Name == "lightmap buffer")
                        ew.Write(3);
                    if (field.Name == "vertex buffer")
                        ew.Write(aux[typeCounter * 6]);
                    else if (field.Name == "lightmap buffer")
                        ew.Write(aux[typeCounter * 6 + 3]);
                    ew.Write(0);
                    ew.Write(0);
                    if (field.Name == "vertex buffer")
                    {
                        pointers.Add(ew.Position);
                        destinations.Add((uint)aux[4 + (typeCounter * 6)]);
                    }
                    else if (field.Name == "lightmap buffer")
                    {
                        pointers.Add(ew.Position);
                        destinations.Add((uint)aux[5 + (typeCounter * 6)]);
                    }
                    ew.Write(0);
                    break;
                case Xpe.FieldType.Char:
                case Xpe.FieldType.CharFlags:
                    ew.Write(Convert.ToByte(value));
                    break;
                case Xpe.FieldType.Float1:
                    ew.Write(Convert.ToSingle(value));
                    break;
                case Xpe.FieldType.Float2:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Float3:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    ew.Write(Convert.ToSingle(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Float4:
                    ew.Write(Convert.ToSingle(array.GetValue(0)));
                    ew.Write(Convert.ToSingle(array.GetValue(1)));
                    ew.Write(Convert.ToSingle(array.GetValue(2)));
                    ew.Write(Convert.ToSingle(array.GetValue(3)));
                    break;
                case Xpe.FieldType.DWordFlags:
                case Xpe.FieldType.Int1:
                    if (value is int)
                        ew.Write(Convert.ToInt32(value));
                    else if (value is uint)
                        ew.Write(Convert.ToUInt32(value));
                    else if (value is short)
                        ew.Write((int)Convert.ToInt16(value));
                    else if (value is ushort)
                        ew.Write((uint)Convert.ToUInt16(value));
                    else
                        throw new Exception("Unknown object type given for Int1/DWordFlags.");
                    break;
                case Xpe.FieldType.Int2:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Int3:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Int4:
                    ew.Write(Convert.ToInt32(array.GetValue(0)));
                    ew.Write(Convert.ToInt32(array.GetValue(1)));
                    ew.Write(Convert.ToInt32(array.GetValue(2)));
                    ew.Write(Convert.ToInt32(array.GetValue(3)));
                    break;
                case Xpe.FieldType.ModelIndexBlock:
                    ew.Write(aux[typeCounter * 5]);
                    ew.Write(aux[1 + (typeCounter * 5)]);
                    ew.Write(aux[4 + (typeCounter * 5)]);
                    ew.Write(5); // TODO: Figure out what the hell this value is
                    break;
                case Xpe.FieldType.ModelVertexBlock:
                    ew.Write(aux[2 + typeCounter * 5]);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Write(aux[3 + ((typeCounter++) * 5)]);
                    break;
                case Xpe.FieldType.Padding:
                    ew.Write(array as byte[]);
                    break;

                case Xpe.FieldType.ScriptData:
                    //int hscCount = (value as DataContainer).variable;
                    //ew.Write(hscCount);
                    //ew.Write(0);
                    //ew.Write(0);
                    //pointers.Add(ew.Position);
                    //destinations.Add(absoluteEnd);
                    //ew.Write(0);
                    //ew.Write(0);
                    //ew.Position = (int)absoluteEnd;
                    //absoluteEnd += (uint)HaloMap.Round(hscCount, 4);
                    //ew.Write((value as DataContainer).data);
                    //break;
                case Xpe.FieldType.Prestructure:
                    int hscpCount = (value as DataContainer).variable;
                    ew.Write(hscpCount);
                    ew.Write(0);
                    ew.Write(0);
                    pointers.Add(ew.Position);
                    destinations.Add(absoluteEnd);
                    ew.Write(0);
                    ew.Write(0);
                    ew.Position = (int)absoluteEnd;
                    absoluteEnd += (uint)hscpCount;
                    ew.Write((value as DataContainer).data);
                    break;

                case Xpe.FieldType.SelfReference:
                    ew.Write(lib.IDByTag(this));
                    break;
                case Xpe.FieldType.Enum:
                case Xpe.FieldType.WordFlags:
                case Xpe.FieldType.Short1:
                    ew.Write(Convert.ToInt16(value));
                    break;
                case Xpe.FieldType.Short2:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    break;
                case Xpe.FieldType.Short3:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    ew.Write(Convert.ToInt16(array.GetValue(2)));
                    break;
                case Xpe.FieldType.Short4:
                    ew.Write(Convert.ToInt16(array.GetValue(0)));
                    ew.Write(Convert.ToInt16(array.GetValue(1)));
                    ew.Write(Convert.ToInt16(array.GetValue(2)));
                    ew.Write(Convert.ToInt16(array.GetValue(3)));
                    break;
                case Xpe.FieldType.SoundDataBlock:
                    ew.Write(aux[typeCounter * 2]);
                    ew.Write(0);
                    ew.Write(aux[1 + ((typeCounter++) * 2)]);
                    ew.Write(0);
                    ew.Write(0);
                    break;
                case Xpe.FieldType.String:
                    ew.Write(Convert.ToString(value), false);
                    break;
                case Xpe.FieldType.TagReference:
                    int tag = Convert.ToInt32(value);
                    if (tag < 0)
                    {
                        if (field.Arguments[0] == "mod2")
                            ew.WriteFourCC("mode");
                        else
                            ew.WriteFourCC(field.Arguments[0]);
                        ew.Write(0);
                        ew.Write(0);
                        ew.Write(-1);
                    }
                    else
                    {
                        string tType = lib.GetClass(tag);
                        if (tType == "mod2")
                            ew.WriteFourCC("mode");
                        else
                            ew.WriteFourCC(tType);
                        ew.Write(lib.StringOffsets[tag]);
                        ew.Write(0);
                        ew.Write(lib.GetID(tag));
                    }
                    break;
            }
            ew.Flush();
        }

        public static int CalculateBitmapSize(short dimensiona, short dimensionb, short dimensionc, short type, short format, short mipMapCount)
        {
            int scalar = dimensiona * dimensionb * (type == 1 ? dimensionc : (short)1)/* * (type == 2 ? 6 : 1)*/;

            for (short x = 1; x <= mipMapCount; x++)
                scalar += (dimensiona / (short)Math.Pow(2.0, (double)x)) * (dimensionb / (short)Math.Pow(2.0, (double)x)) * (type == 1 ? (dimensionc / (short)Math.Pow(2.0, (double)x)) : (short)1)/* * (type == 2 ? 6 : 1)*/;

            switch (format)
            {
                case 0:
                case 1:
                case 2:
                    return HaloMap.Round(scalar, 0x80) * (type == 2 ? 6 : 1);
                case 3:
                    return HaloMap.Round(scalar * 2, 0x80) * (type == 2 ? 6 : 1);
                case 4:
                case 5:
                    throw new Exception("Invalid bitmap format.");
                case 6:
                    return HaloMap.Round(scalar * 2, 0x80) * (type == 2 ? 6 : 1);
                case 7:
                    throw new Exception("Invalid bitmap format.");
                case 8:
                case 9:
                    return HaloMap.Round(scalar * 2, 0x80) * (type == 2 ? 6 : 1);
                case 10:
                case 11:
                    return HaloMap.Round(scalar * 4, 0x80) * (type == 2 ? 6 : 1);
                case 12:
                case 13:
                    throw new Exception("Invalid bitmap format.");
                case 14:
                    return HaloMap.Round(scalar / 2, 0x80) * (type == 2 ? 6 : 1);
                case 15:
                case 16:
                case 17:
                    return HaloMap.Round(scalar, 0x80) * (type == 2 ? 6 : 1);
                default:
                    throw new Exception("Unknown bitmap format.");
            }
        }
        public static int CalculateMipmapSize(short dimensiona, short dimensionb, short dimensionc, short type, short format, short mipMapCount)
        {
            int scalar = (dimensiona / (short)Math.Pow(2.0, (double)mipMapCount)) * (dimensionb / (short)Math.Pow(2.0, (double)mipMapCount)) * (type == 1 ? (dimensionc / (short)Math.Pow(2.0, (double)mipMapCount)) : (short)1)/* * (type == 2 ? 6 : 1)*/;

            switch (format)
            {
                case 0:
                case 1:
                case 2:
                    return scalar * (type == 2 ? 6 : 1);
                case 3:
                    return scalar * 2 * (type == 2 ? 6 : 1);
                case 4:
                case 5:
                    throw new Exception("Invalid bitmap format.");
                case 6:
                    return scalar * 2 * (type == 2 ? 6 : 1);
                case 7:
                    throw new Exception("Invalid bitmap format.");
                case 8:
                case 9:
                    return scalar * 2 * (type == 2 ? 6 : 1);
                case 10:
                case 11:
                    return scalar * 4 * (type == 2 ? 6 : 1);
                case 12:
                case 13:
                    throw new Exception("Invalid bitmap format.");
                case 14:
                    return scalar / 2 * (type == 2 ? 6 : 1);
                case 15:
                case 16:
                case 17:
                    return scalar * (type == 2 ? 6 : 1);
                default:
                    throw new Exception("Unknown bitmap format.");
            }
        }

        public Xpe Xpe
        {
            get { return xpe; }
        }
    }
}
